/*
 * Copyright (c) 2008-2011, The KiWi Project (http://www.kiwi-project.eu)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * - Neither the name of the KiWi Project nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package kiwi.core.services.triplestore;


import kiwi.core.api.config.ConfigurationService;
import kiwi.core.api.content.ContentItemService;
import kiwi.core.api.facading.FacadingService;
import kiwi.core.api.triplestore.KnowledgeSpaceService;
import kiwi.core.api.triplestore.LiteralService;
import kiwi.core.api.triplestore.ResourceService;
import kiwi.core.api.triplestore.TripleStore;
import kiwi.core.api.user.UserService;
import kiwi.core.events.SpaceEvent;
import kiwi.core.exception.KnowledgeSpaceException;
import kiwi.core.model.Constants;
import kiwi.core.model.rdf.KiWiNode;
import kiwi.core.model.rdf.KiWiResource;
import kiwi.core.model.rdf.KiWiTriple;
import kiwi.core.model.rdf.KiWiUriResource;
import kiwi.core.model.user.KiWiUser;
import kiwi.core.qualifiers.event.Created;
import kiwi.core.qualifiers.event.Removed;
import kiwi.core.qualifiers.event.Updated;
import kiwi.core.qualifiers.kspace.*;
import kiwi.core.qualifiers.user.CurrentUser;
import org.slf4j.Logger;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Event;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.*;

/**
 * The knowledge space service offers convenience functions for working with KiWi knowledge spaces. Lowlevel
 * manipulation of knowledge spaces is offered by TripleStore.
 * <p/>
 * A knowledgespace or (other name) namedgraph represent a own graph for a separation of the whole data in the
 * triplestore. In other words: the relationship between triples and a knowledgespace is a 1 to N relationship. Every
 * triple is though the context connect to exactly one knowledgespace.
 * <p/>
 * Every knowledgespace has own access rights, triples, reasoning rules and other metadata.
 * <p/>
 * You can create knowledgespaces for user, for imported ontologies, own created content, inferred triples and
 * systemdata
 * <p/>
 * every new triple without information of a knowledgespace is connect with the context to the defaultspace
 * <p/>
 * A knowledgespace is in a hierarchical structure leaf nodes are knowledgespaces and nodes are categoryspace.
 * A categoryspace can not contain triple.  Only
 * in the case of a knowledgespace as root space is it possible that a knowledgespace has not a categoryspace.
 * <p/>
 * For Example:                             CategorySpace1                  Knowledge3
 *                                          |           |
 *                                      Knowledge1  Category2
 *                                                      |
 *                                                  Knowledge2
 *
 * @author Stefan
 */
@Named("knowledgeSpaceService")
@ApplicationScoped
public class KnowledgeSpaceServiceImpl implements KnowledgeSpaceService, Serializable {

    // the logger used by this KiWi system
    @Inject
    private Logger log;

    // the triple store used by this KiWi system
    @Inject
    private TripleStore tripleStore;


    // the content item service used by this KiWi system
    @Inject
    private ContentItemService contentItemService;

    // the user which look for methods of this service
    @Inject
    @CurrentUser
    private KiWiUser currentUser;

    // the configuration service used by this KiWi system
    @Inject
    private ConfigurationService configurationService;

    // the resource service used by this KiWi system
    @Inject
    private ResourceService resourceService;

    // the facading service used by this KiWi system
    @Inject
    private FacadingService facadingService;

    // the user service used by this KiWi system
    @Inject
    private UserService userservice;

    @Inject
    private LiteralService literalService;

    //****************************************
    // Event container for update, create and remove actions. YOU NEED the use of this variables after every
    // update/create/remove action.
    //****************************************
    @Inject
    @Created
    private Event<SpaceEvent> itemCreatedEvent;

    @Inject
    @Updated
    private Event<SpaceEvent> itemUpdatedEvent;

    @Inject
    @Removed
    private Event<SpaceEvent> itemRemovedEvent;

    //****************************************
    // Get spaces
    //****************************************

    /**
     * Get all Categoryspaces and Knowledgespaces stored in this system and are not deleted.
     *
     * @return new ArrayList() if the database empty and all spaces if the database is not empty
     */
    @Override
    public List<KiWiUriResource> getSpaces() {

        List<KiWiUriResource> spaces = new ArrayList();

        spaces.addAll(((List) this.getCategorySpaces()));
        spaces.addAll(((List) this.getKnowledgeSpaces()));

        return spaces;
    }

    /**
     * Get spaces which has not a parent node
     *
     * @return root spaces
     */
    @Override
    public List<KiWiUriResource> getRootSpaces() {
        List<KiWiUriResource> allSpaces = this.getSpaces();
        List<KiWiUriResource> rootSpaces = new ArrayList<KiWiUriResource>();

        // Find spaces without a associated categoryspace
        for (KiWiUriResource space : allSpaces) {

            if (getCategory(space) == null) {
                rootSpaces.add(space);
            }
        }

        return rootSpaces;

    }

    @Override
    public KiWiUriResource getCategory(KiWiUriResource space) {

        List<KiWiTriple> triples;
        try {
            triples = resourceService.listOutgoing(space, Constants.NS_SKOS+"broader");
        } catch(Exception e) {
            return null;
        }

        for(KiWiTriple triple : triples) {
           return (KiWiUriResource) triple.getObject();
        }

        return null;
    }

    /**
     * Get all children of the given Categoryspace. If space null is it will return a empty List.
     *
     * @param space Category with there SubSpaces
     * @return a List of all sub spaces (type: categoryspace and/or knowledgespace)
     */
    @Override
    public List<KiWiUriResource> getChildren(KiWiUriResource space) {

        if(space == null) return new ArrayList();

        List<KiWiTriple> triples = resourceService.listIncoming(space);
        List<KiWiUriResource> result = new ArrayList();

        for(KiWiTriple triple : triples) {
            if(triple.getProperty().toString().equals(Constants.NS_SKOS+"broader")) {
                result.add((KiWiUriResource)triple.getSubject());
            }
        }

        return result;

    }

    /**
     * Get a space (category and knowledge) with the given title
     *
     * @param title of the space
     * @return space with this title. If there is no space with the given title return this method null
     */
    @Override
    public KiWiUriResource getSpaceByTitle(String title) {

        KiWiUriResource result1 = this.getKnowledgeSpaceByTitle(title.toLowerCase());

        KiWiUriResource result2 = this.getCategorySpaceByTitle(title.toLowerCase());

        if (result1 != null && result2 != null) {
            log.warn("it exists a knowledgespace (" + result1.getKiwiIdentifier() + ") and categoryspace (" +
                    result2.getKiwiIdentifier() + ") with a same title");
        }

        return result1 == null ? result2 : result1;
    }

    /**
     * Get a space (category and knowledge) with the given uri
     *
     * @param uri of the space
     * @return space with this title. If there is no space with the given uri return this method null
     */
    @Override
    public KiWiUriResource getSpaceByUri(String uri) {
        KiWiUriResource result1 = this.getKnowledgeSpaceByUri(uri);

        KiWiUriResource result2 = this.getCategorySpaceByUri(uri);

        if (result1 != null && result2 != null) {
            log.warn("it exists a knowledgespace (" + result1.getKiwiIdentifier() + ") and categoryspace (" +
                    result2.getKiwiIdentifier() + ") with a same uri");
        }

        return result1 == null ? result2 : result1;
    }


    /**
     * Get all CategorySpaces
     *
     * @return all categoryspaces of the database
     */
    @Override
    public List<KiWiUriResource> getCategorySpaces() {

        KiWiUriResource prop = resourceService.getUriResource(KnowledgeSpaceService.SPACE_KIND);

        //TODO: Problem if we construct the defaultspace first time, this is a dirty workaround
        if(prop == null) return new ArrayList();

        KiWiNode categoryLiteral = literalService.createLiteral("categoryspace");
        Set<KiWiTriple> categorySpaces = tripleStore.listTriples(null
                ,prop
                ,categoryLiteral,null);

        Set<KiWiUriResource> categories = new HashSet();

        for(KiWiTriple triple : categorySpaces) {
            if(triple.getSubject().isUriResource()) {
                categories.add((KiWiUriResource) triple.getSubject());
            }
        }

        return new ArrayList(categories);
    }

    /**
     * Get the categoryspace with the given uri
     *
     * @param uri of the categoryspace
     * @return categoryspace with this uri. If there is no categoryspace with the given uri return this method null
     */
    @Override
    public KiWiUriResource getCategorySpaceByUri(String uri) {

        for(KiWiUriResource category : this.getCategorySpaces()) {
            if(category.getUri().equals(uri)) {
                return category;
            }
        }

        return null;
    }

    /**
     * Get the knowledgespace with the given uri
     *
     * @param uri of the categoryspace
     * @return categoryspace with this uri. If there is no categoryspace with the given uri return this method null
     */
    @Override
    public KiWiUriResource getKnowledgeSpaceByUri(String uri) {

        for(KiWiUriResource knowledgeSpace : this.getKnowledgeSpaces()) {
            if(knowledgeSpace.getUri().equals(uri)) {
                return knowledgeSpace;
            }
        }

        return null;
    }


    /**
     * Get the categoryspace with the given title
     *
     * @param title of the categoryspace
     * @return categoryspace with this title. If there is no categoryspace with the given title return this method null
     */
    @Override
    public KiWiUriResource getCategorySpaceByTitle(String title) {

        KiWiResource titleProp = resourceService.createUriResource(Constants.NS_KIWI_CORE + "title");

        Collection<KiWiResource> spaces = resourceService.listResourcesByProperty(Constants.NS_KIWI_CORE + "title", title.toLowerCase());

        Collection<KiWiResource> categories = (List) this.getCategorySpaces();

        List<KiWiResource> spacesResult = this.makeIntersection(spaces, categories);

        if (spacesResult.size() == 1) {
            return (KiWiUriResource) spacesResult.get(0);
        } else if (spacesResult.size() > 1) {
            log.warn("no unique space found for title " + title.toLowerCase());
            return (KiWiUriResource) spacesResult.get(0);
        } else {
            return null;
        }
    }

    /**
     * Get all knowledgespaces stored in this KiWi system
     *
     * @return all knowledgespaces
     */
    @Override
    public List<KiWiUriResource> getKnowledgeSpaces() {

        KiWiResource kindSpace = resourceService.getUriResource(KnowledgeSpaceService.SPACE_KNOWLEDGESPACE);

        //TODO: Problem if we construct the defaultspace first time, this is a dirty workaround
        if(kindSpace == null) return new ArrayList();

        return new ArrayList(resourceService.listResources(kindSpace));
    }

    /**
     * Get the Knowledgespace which is stored in this KiWi system with the given title
     *
     * @param title title of this knowledgespace
     * @return A Knowledgespace with the given title, if it give not a knowledgespace with the title then "null"
     */
    @Override
    public KiWiUriResource getKnowledgeSpaceByTitle(String title) {

        try {
            KiWiResource kindSpace = resourceService.createUriResource(KnowledgeSpaceService.SPACE_KNOWLEDGESPACE);
            KiWiUriResource titleProp = resourceService.createUriResource(Constants.NS_KIWI_CORE + "title");

            for(KiWiTriple triple : tripleStore.listTriples(null, titleProp, literalService.createLiteral(title.toLowerCase()),null)) {
                if(resourceService.getTypes(triple.getSubject()).contains(kindSpace)) {
                    return (KiWiUriResource) triple.getSubject();
                }
            }

        } catch (Exception e) {
            return null;
        }
        return null;

    }

    private List<KiWiResource> makeIntersection(Collection<KiWiResource> resourceFirst, Collection<KiWiResource> resourceSecond) {

        // TODO: in some cases (Removing and then create the same instance) have items2 and spacetype the same object (after uri) but the instance is different!!!
        // some problems by removing and caching service?!?!? It is very dirty here !!!
        Collection<String> intersectionItems = convertIntoUriStrings(new ArrayList(resourceSecond));
        intersectionItems.retainAll(convertIntoUriStrings(new ArrayList(resourceFirst)));

        return convertIntoResource(new ArrayList(intersectionItems));
    }

    /**
     * Convert a list of resources in a list of Strings with the uris which represent the resources
     *
     * @param resources A list of all resource
     * @return a list of all uris in this list
     */
    private List<String> convertIntoUriStrings(List<KiWiResource> resources) {

        List<String> stringResources = new ArrayList();
        for(KiWiResource resource : resources) {
            stringResources.add(((KiWiUriResource) resource).getUri());
        }
        return stringResources;
    }

    /**
     * Convert a list of strings with given uris in a list of KiWiResources
     *
     * @param stringResources which a transfer to KiWiResources
     * @return a list of resources of the given Strings
     */
    private List<KiWiResource> convertIntoResource(List<String> stringResources) {
        List<KiWiResource> resources = new ArrayList();
        for(String stringResource : stringResources) {
            resources.add(resourceService.createUriResource(stringResource));
        }
        return resources;
    }

    /**
     * Return a list of spaces which have the given propertyUri with the given propLabel and are of the given type.
     * Type means all spaces type of the constants of KnowledgespaceService + "categoryspace".
     *
     * @param propertyUri Uri of the property
     * @param propLabel   label of the property
     * @return list of all spaces with this properties
     */
    private List<KiWiUriResource> getSpacesOfProperty(String propertyUri, String propLabel) {

        Collection<KiWiResource> withProperty = resourceService.listResourcesByProperty(propertyUri, propLabel.toLowerCase());
        Collection<KiWiResource> spaces = resourceService.listResources(resourceService.createUriResource(
                KnowledgeSpaceService.SPACE_KNOWLEDGESPACE));

        List<KiWiUriResource> itemsList = (List) makeIntersection(spaces, withProperty);

        return itemsList;
    }

    /**
     * Return a list of spaces which have the given propertyUri with the given propLabel and are of the given type.
     * Type means all spaces type of the constants of KnowledgespaceService + "categoryspace"
     *
     * @param propertyUri Uri of the property
     * @param prop        property for the search task
     * @return list of all spaces with this properties
     */
    private List<KiWiUriResource> getSpacesOfProperty(String propertyUri, KiWiNode prop) {
        Collection<KiWiResource> withProperty = resourceService.listResourcesByProperty(propertyUri, prop);
        Collection<KiWiResource> spaces = resourceService.listResources(resourceService.createUriResource(
                KnowledgeSpaceService.SPACE_KNOWLEDGESPACE));

        List<KiWiUriResource> itemsList = (List) makeIntersection(spaces, withProperty);

        return itemsList;
    }

    /**
     * Get the personal/private knowledgespace of a given user.
     *
     * @param user user with a personal/private knowledgespace
     * @return personal/private knowledgespace of the user
     * @throws KnowledgeSpaceException if user null or not persist is or
     *                                 if user has not a personal knowledgespace
     */
    @Override
    public KiWiUriResource getPersonalKnowledgeSpace(KiWiUriResource user) {

        if(user == null) return null;

        Collection<KiWiResource> spaces = (Collection) this.getKnowledgeSpaces();

        Collection<KiWiResource> ownerUser = (Collection)
            resourceService.listResourcesByProperty(Constants.NS_KIWI_CORE_KNOWLEDGESPACE + "owner",user);

        List<KiWiUriResource> result = (List) this.makeIntersection(spaces, ownerUser);

        return result == null || result.size() == 0 ? null : result.get(0);
    }



    //****************************************
    // Create Spaces
    //****************************************

    /**
     * Create a knowledgespace with the given title and type, if the type is a null value or
     * single space like Defaultspace it will be return a knowledgespaceexception.
     *
     * @param uri of the new knowledgespace
     * @return the new knowledgespace otherwise is it null
     * @throws KnowledgeSpaceException
     */
    @Override
    public KiWiUriResource createKnowledgeSpaceByUri(String uri) throws KnowledgeSpaceException {

        return createSpaceByUri(uri, false);
    }

    /**
     * Create a knowledgespace with the given title, if the title is null, exist as categoryspace
     * or empty it will throw a KnowledgeSpaceException
     *
     * @param uri of the new categoryspace
     * @return the new categoryspace otherwise is it null
     * @throws KnowledgeSpaceException title is invalid
     */
    @Override
    public KiWiUriResource createCategorySpaceByUri(String uri) throws KnowledgeSpaceException {

        return createSpaceByUri(uri, true);
    }


    /**
     * Create a knowledgespace with the given title and type, if the type is a null value or
     * single space like Defaultspace it will be return the old instance.
     *
     * @param title of the new knowledgespace
     * @return the new knowledgespace otherwise the old instance
     * @throws KnowledgeSpaceException if title null
     */
    @Override
    public KiWiUriResource createKnowledgeSpace(String title) throws KnowledgeSpaceException {

        return (KiWiUriResource) createSpaceByTitle(title.toLowerCase(), false);
    }

    /**
     * Create a knowledgespace with the given title, if the title is null, exist as categoryspace
     * or empty it will throw a KnowledgeSpaceException
     *
     * @param title of the new categoryspace
     * @return the new categoryspace otherwise is it null
     * @throws KnowledgeSpaceException title is invalid
     */
    @Override
    public KiWiUriResource createCategorySpace(String title) throws KnowledgeSpaceException {

        return (KiWiUriResource) createSpaceByTitle(title.toLowerCase(), true);
    }

    /**
     * Create a space by the given title, type and categorySpace.
     *
     * @param title titel of the new space
     * @param categorySpace is it a category?
     * @return the new space instance
     * @throws KnowledgeSpaceException if title invalid
     */
    private KiWiUriResource createSpaceByTitle(String title, boolean categorySpace) throws KnowledgeSpaceException {
        if (title == null || title.equals("")) {
            log.error("uri is null or empty by categoryspace create");
            throw new KnowledgeSpaceException("uri is null or empty by categoryspace create");
        }

        return createSpaceByUri(configurationService.getBaseUri()
                + (categorySpace ? "categoryspace" : "knowledgespace") + "/" + title.toLowerCase().replace(" ", "_"), categorySpace);
    }

    /**
     * create a space with title, type and the answer for "is this space a knowledgespace?"
     * if this is a categoryspace type can be null otherwise it will check  how the title by invalid values it will
     * return a knoweledgepsaceexception
     *
     * @param uri         of the new space
     * @param categorySpace is it a categoryspace?
     * @return the new space
     * @throws KnowledgeSpaceException if there is something wrong
     */
    private KiWiUriResource createSpaceByUri(String uri, boolean categorySpace) throws KnowledgeSpaceException {
        if (uri == null || uri.equals("")) {
            log.error("uri is null or empty by categoryspace create");
            throw new KnowledgeSpaceException("uri is null or empty by categoryspace create");
        }

        KiWiUriResource space = null;

        //Give it a space with the given title and of the given type?
        if (categorySpace) {

            // TODO: TransientObjectException: object references an unsaved transient instance
            // - save the transient instance before flushing (just first time)
            try {
                space = this.getCategorySpaceByUri(uri);
            } catch (Exception e) {

            }
        } else {
            try {
                space = this.getKnowledgeSpaceByUri(uri);
            } catch (Exception e) {

            }
        }

        if (space == null) {

            space = createKnowledgespaceResource(uri, categorySpace);

            //TODO: fix sometimes defaultspace creationTime is a Timestamp what a invalid value is
            if(space.getCreated() instanceof Timestamp) {
                space.setCreated(new Date(space.getCreated().getTime()));
            }



            try {
                KiWiResource user = null;

                // TODO: getResource() is currently null
                try {
                    user = this.currentUser.getResource();
                } finally {
                    if(user == null)
                        user = resourceService.createUriResource(Constants.NS_KIWI_CORE + "user/" + "anonymous");
                }

                resourceService.addOutgoingNode(space,"<"+Constants.NS_KIWI_CORE+"hasAuthor>", user);
                resourceService.addOutgoingNode(space,"<"+Constants.NS_KIWI_CORE+"createdOn>", literalService.createLiteral(space.getCreated()));
                resourceService.addOutgoingNode(space,"<"+Constants.NS_KIWI_CORE+"title>", literalService.createLiteral(space.getTitle().toLowerCase()));
            } catch(Exception e) {}

            itemCreatedEvent.fire(new SpaceEvent(space));
        } else {
            return space;
        }

        return space;
    }

    /**
     * Create a knowledgespace by the given uri and type
     *
     * @param uri uri of the new knowledgespace
     * @return A new KiWiUriResource which are represent the uri and type which are given by the parameter
     * @throws KnowledgeSpaceException if is something wrong by creating the graphcontent
     */
    private KiWiUriResource createKnowledgespaceResource(String uri, boolean isCategorySpace) throws KnowledgeSpaceException {
        KiWiUriResource graphcontentURI = resourceService.createUriResource(configurationService.getBaseUri()
                + "knowledgespace/systemspace");

        KiWiUriResource resource = resourceService.createUriResource(uri, graphcontentURI);

        KiWiUriResource skosConceptType = resourceService.createUriResource(Constants.NS_SKOS+"Concept");
        resourceService.addType(resource, skosConceptType);

        if (!isCategorySpace) {
            KiWiUriResource knowledgeSpace = resourceService.createUriResource(KnowledgeSpaceService.SPACE_KNOWLEDGESPACE);
            resourceService.addType(resource, knowledgeSpace);

            this.createSpaceByTitle("systemspace", false); // create the graphcontent space if necessary
            this.getDefaultKnowledgeSpace(); // create the defaultknowledgespace if necessary
        } else {
            try {
                resourceService.removeProperty(resource,"<" + KnowledgeSpaceService.SPACE_KIND + ">");
                resourceService.addOutgoingNode(resource, "<" + KnowledgeSpaceService.SPACE_KIND + ">",literalService.createLiteral("categoryspace"));
            } catch(Exception e) {}
        }

        return resource;
    }

    //****************************************
    // remove and set
    //****************************************

    /**
     * Remove a space with the given triples or ContentItems.
     * It Removes everything is connect with the space and the items in this space. If it is a Categoryspace
     * it will remove revers all children.
     *
     * @param space which is to remove
     * @throws KnowledgeSpaceException if space is a invalid value (null, empty or null title)
     */
    @Override
    public void removeSpaceWithContent(KiWiUriResource space) throws KnowledgeSpaceException {

        checkSpaceForRemove(space);
        removeSpaceWithContentUnCheck(space);
    }

    /**
     * Check the space for singelton or if it is empty and give a knowledgespaceException
     *
     * @param space space which are test for remove process
     * @throws KnowledgeSpaceException if a singelton as child is or if space null is.
     */
    private void checkSpaceForRemove(KiWiUriResource space) throws KnowledgeSpaceException {

        if (space == null) {

            log.error("space is null");
            throw new KnowledgeSpaceException("can not remove space, space is null");
        }

//        List<KiWiUriResource> allChildren = getChildren((KiWiUriResource) space);
//
//        for (KiWiUriResource nextSpace : allChildren) {
//            checkSpaceForRemove(nextSpace);
//        }
    }

    /**
     * Remove a space without checking for a valid remove operation
     *
     * @param space which are check
     * @throws KnowledgeSpaceException
     */
    private void removeSpaceWithContentUnCheck(KiWiUriResource space) throws KnowledgeSpaceException {

        KiWiResource type = resourceService.createUriResource(Constants.NS_KIWI_CORE_KNOWLEDGESPACE + "categoryspace");

        List<KiWiUriResource> allChildren = getChildren(space);

        for (KiWiUriResource nextSpace : allChildren) {
            removeSpaceWithContent(nextSpace);
        }

        Collection<KiWiResource> resources = this.resourceService.listResourcesBySpace((KiWiUriResource) space);
        Iterator<KiWiResource> resourcesIter = resources.iterator();

        // Remove all Resources of the knowledgespace
        while (resourcesIter.hasNext()) {
            KiWiResource item = resourcesIter.next();

            resourceService.removeResource(item, (KiWiUriResource) space);
        }

        for(KiWiTriple triple : tripleStore.listTriples(null,null,null,space,true)) {
            tripleStore.removeTriple(triple);
        }

        // And remove all triples (it is secure)
        KiWiResource item = space;
        resourceService.removeResource(item);
        itemRemovedEvent.fire(new SpaceEvent(space));
    }

    /**
     * Move the given triples to a knowledgespace
     *
     * @param triplesToMove triple which are move to a new knowledgespace
     * @param space space which will contain the new triples
     * @throws KnowledgeSpaceException if triplesToMove or space are invalid values
     */
    @Override
    public void moveContentTo(List<KiWiTriple> triplesToMove, KiWiUriResource space) throws KnowledgeSpaceException {

        if (space == null || space.getTitle() == null || space.getTitle().equals("") || triplesToMove == null) {

            log.warn("empty parameter by move content to a knowledgespace");
            throw new KnowledgeSpaceException("can not move triples to: " + space + " (empty parameter)");
        }

        for (KiWiTriple currentTriple : triplesToMove) {

            moveContentTo(currentTriple, space);
        }
    }

    /**
     * Move the given triples to a knowledgespace
     *
     * @param tripleToMove triple which are move to a new knowledgespace
     * @param space space which will contain the new triples
     * @throws KnowledgeSpaceException if triplesToMove or space are invalid values
     */
    @Override
    public void moveContentTo(KiWiTriple tripleToMove, KiWiUriResource space) throws KnowledgeSpaceException {

        if (space == null || space.getTitle() == null || space.getTitle().equals("") || tripleToMove == null) {

            log.warn("empty parameter by move content to a knowledgespace");
            throw new KnowledgeSpaceException("can not move triples to: " + space + " (empty parameter)");
        }

        tripleStore.removeTriple(tripleToMove);
        tripleStore.createTriple(tripleToMove.getSubject(), tripleToMove.getProperty(),
                tripleToMove.getObject(), (KiWiUriResource) space);

    }

    /**
     * Set a space for a user as a private knowledgeSpace. If user null space owner is nobody !!!
     *
     * @param user  user for the private knowledgeSpace
     * @param space title of the space which is the new private/personal knowledgespace
     * @throws KnowledgeSpaceException if user null or not persist is
     *                                 or if space has a type or user has a personal knowledgeSpace
     */
    @Override
    public KiWiUriResource setPersonalKnowledgeSpace(KiWiUriResource user, KiWiUriResource space) throws KnowledgeSpaceException {

        KiWiUriResource personalSpace = getPersonalKnowledgeSpace((KiWiUriResource) user);

        if (personalSpace != null && user != null) {
            log.error("user " + user.getTitle() + " has a private knowledgespace " + space.getTitle());
            throw new KnowledgeSpaceException("user " + user.getTitle() + " has a private knowledgespace " + space.getTitle());
        } else {

            try {
                resourceService.removeProperty(space, "<" + Constants.NS_KIWI_CORE_KNOWLEDGESPACE + "owner" + ">");

                if(user != null) {
                    resourceService.addOutgoingNode(space, "<" + Constants.NS_KIWI_CORE_KNOWLEDGESPACE + "owner" + ">",user);
                }
            } catch(Exception e) {}

            itemUpdatedEvent.fire(new SpaceEvent(space));
            return space;
        }

    }



    //****************************************
    // get/create default, inferred
    //****************************************


    /**
     * Return the knowledge space that is currently selected for write access. The currently active knowledge space
     * is either the default knowledge space or explicitly passed as argument ctx to web service calls.
     *
     * @return a KiWiUriResource representing the current knowledge space
     */
    @Override
    @Produces @RequestScoped @CurrentKnowledgeSpace
    public KiWiUriResource getCurrentKnowledgeSpace() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * Return the knowledge space used for storing system information.
     *
     * @return a KiWiUriResource representing the system knowledge space
     */
    @Override
    @Produces @RequestScoped @SystemKnowledgeSpace
    public KiWiUriResource getSystemKnowledgeSpace() {
        return resourceService.createUriResource(configurationService.getBaseUri() + "knowledgespace/system");
    }

    /**
     * Return the set of knowledge spaces that is currently active for reading. The set of active knowledge spaces
     * is either selected explicitly in web service calls or it consists of all knowledge spaces.
     *
     * @return a set of KiWiUriResources indicating the active knowledge spaces
     */
    @Override
    @Produces @RequestScoped @ActiveKnowledgeSpaces
    public Set<KiWiUriResource> getActiveKnowledgeSpaces() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * Get the uri of the inferred knowledgespace
     *
     * @return uri of this inferred knowledgespace
     */
    @Override
    @Produces @RequestScoped @InferredKnowledgeSpace
    public KiWiUriResource getInferredKnowledgeSpace() {
        return resourceService.createUriResource(configurationService.getBaseUri() + "knowledgespace/inferred");
    }

    /**
     * Get the uri of the default knowledgespace
     *
     * @return
     */
    @Override
    @Produces @RequestScoped @DefaultKnowledgeSpace
    public KiWiUriResource getDefaultKnowledgeSpace() {
        return resourceService.createUriResource(configurationService.getBaseUri() + "knowledgespace/default");
    }

    //****************************************
    // update methods
    //****************************************

    /**
     * Update the title of this space
     *
     * @param item item which are updated
     * @param title new title of this item
     */
    @Override
    public void updateTitle(KiWiUriResource item, String title) {
        if(title == null) throw new IllegalArgumentException("title is null by updateTitle of resource " + item.getUri());

        item.setTitle(title.toLowerCase());

        try {
            resourceService.setProperty(item,Constants.NS_KIWI_CORE+"title", title.toLowerCase());
        } catch(Exception e) {}

        itemUpdatedEvent.fire(new SpaceEvent(item));
    }

    /**
     * Update the category of this space
     *
     * @param item item which are updated
     * @param category new category of this item
     */
    @Override
    public void updateCategorySpace(KiWiUriResource item, KiWiUriResource category) throws KnowledgeSpaceException {

        if(item == null) return;

        try {
            resourceService.removeProperty(item, "<"+Constants.NS_SKOS+"broader>");

            if(category != null) {
                resourceService.addOutgoingNode(item, "<"+Constants.NS_SKOS+"broader>", category);
            }

        } catch(Exception e){}

        itemUpdatedEvent.fire(new SpaceEvent(item));
    }
}
